﻿using System;
using System.Collections.Generic;
using System.Linq;
using Kugar.Core.ExtMethod;
using Kugar.Core.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Kugar.Tool.Web.RequestFieldValidation;

namespace Kugar.Tool.Web.RequestFieldValidation.Rules
{
    /// <summary>
    /// 将一个变量或者一个值包装为一个field,方便进行校验,如果要使用该功能,请预先使用
    /// app.UseStaticHttpContext();
    /// </summary>
    public static class WrapperToField{
            public static IRequestField<T> From<T>(T value,string fieldName,string title=""){

                var collection=  ((RequestFieldCollection) Kugar.Core.Web.HttpContext.Current.RequestServices.GetService(typeof(RequestFieldCollection)));

                var field=RequestField<T>. FromValue(fieldName,value,title:title);
                collection.AddField(fieldName,field);

                return field;
            }

            public static IRequestField<T?> From<T>(T? value,string fieldName,string title="")  where T:struct
            {
                var collection=  ((RequestFieldCollection) Kugar.Core.Web.HttpContext.Current.RequestServices.GetService(typeof(RequestFieldCollection)));

                var field=RequestField<T>. FromValue(fieldName,value,title:title);
                collection.AddField(fieldName,field);

                return field;
            }
    }

    public static class RequestFieldValidateRuleExt
    {
        /// <summary>
        /// 获取一个字段对象,并且读取该字段对象的值,并转换为对应的类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="fieldName"></param>
        /// <param name="defaultValue"></param>
        /// <param name="format">字段转换时的格式化字符串,目前只用于datetime类型</param>
        /// <returns></returns>
        public static IRequestField<T> GetField<T>(this HttpRequest request, string fieldName, T defaultValue = default(T),string format="", string fieldTitle = "",bool autoDecode=false)
        {
            var valueStr = request.GetString(fieldName,"", autoDecode: autoDecode);
            
            T value = RequestFieldServices.ConvertTo<T>(valueStr, format, defaultValue);

            var field= ((RequestFieldCollection) request.HttpContext.RequestServices.GetService(typeof(RequestFieldCollection)))
                .GetOrAddField<T>(fieldName, value, request, fieldTitle,format);
            
            return field;
        }

        /// <summary>
        /// 获取一个数组数据,数据必须是用逗号分隔,读取之后,将会自动切分为对应的数组数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static IRequestArrayField<IEnumerable<TItem>, TItem> GetArrayValueField<TItem>(this HttpRequest request, string fieldName, IEnumerable<TItem> defaultValue =null, string format = "",string fieldTitle="")
        {
            List<TItem> value = new List<TItem>();

            var str = request.GetString(fieldName);
            if (string.IsNullOrWhiteSpace(str))
            {
                value = defaultValue?.ToList();
            }
            else
            {
                var arr = str.Split(',').Where(x => !string.IsNullOrWhiteSpace(x)).ToArrayEx();
                if (!arr.HasData())
                {
                    value = defaultValue?.ToList();
                }
                else
                {
                    foreach (var item in arr)
                    {
                        value.Add(RequestFieldServices.ConvertTo<TItem>(item, format, default));
                    }
                }
            }

            return ((RequestFieldCollection)request.HttpContext.RequestServices.GetService(typeof(RequestFieldCollection)))
                .GetOrAddArrayField(fieldName, value.ToArrayEx(), request, fieldTitle);
        }

        
        /// <summary>
        /// 判断值是否大于或等于指定值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <typeparam name="TCompareValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareValue"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> GTE<TFieldValue,TCompareValue>(this IRequestField<TFieldValue> field,TCompareValue compareValue,string errorMsg=null,bool when=true) where TCompareValue :IComparable where TFieldValue:IComparable
        {
            if (!when) return field;
            
            return addCompareRule<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.GTE, errorMsg);
        }

        public static IRequestField<TFieldValue?> GTE<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue? compareValue, string errorMsg = null, bool when = true) where TCompareValue :struct  where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule3<TFieldValue, TCompareValue>(field,compareValue, CompareOpt.GTE, errorMsg);
        }

        public static IRequestField<TFieldValue?> GTE<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule2<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.GTE, errorMsg);
        }

        /// <summary>
        /// 判断值是否大于指定值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <typeparam name="TCompareValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareValue"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> GT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue :IComparable where TFieldValue : IComparable
        {
            if (!when) return field;

            return addCompareRule<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.GT, errorMsg);
        }

        public static IRequestField<TFieldValue?> GT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue? compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule3<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.GT, errorMsg);
        }

        public static IRequestField<TFieldValue?> GT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule2<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.GT, errorMsg);
        }


        /// <summary>
        /// 判断值是否小于指定值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <typeparam name="TCompareValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareValue"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> LT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : IComparable where TFieldValue : IComparable
        {
            if (!when) return field;

            return addCompareRule<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LT, errorMsg);
        }

        public static IRequestField<TFieldValue?> LT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue? compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule3<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LT, errorMsg);
        }

        public static IRequestField<TFieldValue?> LT<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule2<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LT, errorMsg);
        }



        /// <summary>
        /// 判断值是否小于或等于指定值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <typeparam name="TCompareValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareValue"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> LTE<TFieldValue, TCompareValue>(this IRequestField<TFieldValue> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : IComparable where TFieldValue : IComparable
        {
            if (!when) return field;

            return addCompareRule<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LTE, errorMsg);
        }


        public static IRequestField<TFieldValue?> LTE<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue? compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule3<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LTE, errorMsg);
        }

        public static IRequestField<TFieldValue?> LTE<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule2<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.LTE, errorMsg);
        }


        /// <summary>
        /// 判断值是否等于指定值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <typeparam name="TCompareValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareValue"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> EQ<TFieldValue, TCompareValue>(this IRequestField<TFieldValue> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue :  IComparable where TFieldValue : IComparable
        {
            if (!when) return field;

            return addCompareRule<TFieldValue, TCompareValue>(field, compareValue,CompareOpt.EQ, errorMsg);
        }

        public static IRequestField<TFieldValue?> EQ<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue? compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule3<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.EQ, errorMsg);
        }

        public static IRequestField<TFieldValue?> EQ<TFieldValue, TCompareValue>(this IRequestField<TFieldValue?> field, TCompareValue compareValue, string errorMsg = null, bool when = true) where TCompareValue : struct where TFieldValue : struct
        {
            if (!when) return field;

            return addCompareRule2<TFieldValue, TCompareValue>(field, compareValue, CompareOpt.EQ, errorMsg);
        }

        /// <summary>
        /// 校验值是否在指定范围内
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="min">最小值</param>
        /// <param name="max">最大值</param>
        /// <param name="isEqualEnabled">是否是闭区间.默认为true</param>
        /// <param name="when"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> Range<TFieldValue>(this IRequestField<TFieldValue> field,
            TFieldValue min, TFieldValue max, bool isEqualEnabled = true,string errorMsg= null, bool when=true) where TFieldValue:IComparable
        {
            if (!when)
            {
                return field;
            }

            if (isEqualEnabled)
            {
                return field.GTE(min, errorMsg).LTE(max, errorMsg);
            }
            else
            {
                return field.GT(min, errorMsg).LT(max, errorMsg);
            }
        }

        /// <summary>
        /// 自定义参数检查函数
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="checkFunc">返回检查结果是否为正确</param>
        /// <param name="errorMsg">错误提示</param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> Must<TFieldValue>(this IRequestField<TFieldValue> field,
            Func<TFieldValue, bool> checkFunc, string errorMsg, bool when = true)
        {
            if (!when) return field;

            var rule = new MustCheckRule<TFieldValue>(checkFunc, errorMsg);
            
            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 判断值是否等于另外一个字段
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareFieldName">用于比较的字段名</param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> EqualToField<TFieldValue>(this IRequestField<TFieldValue> field,
            string compareFieldName,string compareFieldTitle="", string errorMsg = null, bool when = true) where TFieldValue:IComparable
        {
            if (!when) return field;

            if (string.IsNullOrWhiteSpace(compareFieldTitle))
            {
                compareFieldTitle = compareFieldName;
            }

            var rule=new EqualToFieldRule<TFieldValue>(compareFieldName,compareFieldTitle, errorMsg);
 
            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 判断值是否等于另外一个字段
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="compareFieldName">用于比较的字段名</param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue?> EqualToField<TFieldValue>(this IRequestField<TFieldValue?> field,
            string compareFieldName, string compareFieldTitle = "", string errorMsg = null, bool when = true) where TFieldValue : struct
        {
            if (!when) return field;

            if (string.IsNullOrWhiteSpace(compareFieldTitle))
            {
                compareFieldTitle = compareFieldName;
            }

            var rule = new EqualToFieldRuleNullable<TFieldValue>(compareFieldName, compareFieldTitle, errorMsg);

            field.AddRule(rule);

            return field;
        }


        /// <summary>
        /// 字段可以为空,但Key必须存在值
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> KeyExists<TFieldValue>(this IRequestField<TFieldValue> field, string errorMsg = null, bool when = true)
        {
            if (!when) return field;

            var rule =new ExistsCheckRule(errorMsg);
            
            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// key对应的值不能为空,或者空格,或者null
        /// </summary>
        /// <param name="field"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static T NotEmpty<T>(this T field, string errorMsg = null, bool when = true) where T:IRequestField
        {
            if (!when) return field;

            var rule = new NotEmptyCheckRule(errorMsg);
            
            field.AddRule(rule);

            return field;
        }
        

        /// <summary>
        /// 数组必须存在值,不能为空
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestArrayField<IEnumerable<TInputValue>, TInputValue> NotEmptyArray<TInputValue>(this IRequestArrayField<IEnumerable<TInputValue>,TInputValue> field, string errorMsg = null)
        {
            var rule = new ArrayFieldNotEmptyRule<TInputValue>(errorMsg);
            
            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 字符串最大长度
        /// </summary>
        /// <param name="field"></param>
        /// <param name="maxLength"></param>
        /// <returns></returns>
        public static IRequestField<string> MaxLength(this IRequestField<string> field, int maxLength,string errorMsg=null, bool when = true,bool asciiLength=false)
        {
            if (!when) return field;

            return Length(field, maxLength, -1, errorMsg);
        }

        /// <summary>
        /// 字符串最小长度
        /// </summary>
        /// <param name="field"></param>
        /// <param name="minLength"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<string> MinLength(this IRequestField<string> field,int minLength, string errorMsg = null, bool when = true, bool asciiLength = false)
        {
            if (!when) return field;

            return Length(field, -1, minLength, errorMsg);
        }

        /// <summary>
        /// 字符串长度范围
        /// </summary>
        /// <param name="field"></param>
        /// <param name="maxLength"></param>
        /// <param name="minLength"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<string> Length(this IRequestField<string> field,int maxLength,int minLength, string errorMsg = null, bool when = true, bool asciiLength = false)
        {
            if (!when) return field;

            var rule = new StringLengthRule(maxLength, minLength, errorMsg, asciiLength);

            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 是否符合EMail规则
        /// </summary>
        /// <param name="field"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<string> EMail(this IRequestField<string> field, string errorMsg = null, bool when = true)
        {
            if (!when) return field;

            var rule = new EMailRule(errorMsg);

            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 是否符合手机号规则
        /// </summary>
        /// <param name="field"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<string> Mobile(this IRequestField<string> field, string errorMsg = null, bool when = true)
        {
            if (!when) return field;

            var rule = new MobileRule(errorMsg);

            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 远程校验,必须返回ResultReturn结构体
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="url"></param>
        /// <param name="method"></param>
        /// <param name="withNames"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> Remote<TFieldValue>(this IRequestField<TFieldValue> field, string url,
            string method = "get", string[] withNames = null, string errorMsg = null, bool when = true)
        {
            if (!when) return field;

            var rule = new ClientRemoteCheckRule(url,method,withNames??Array.Empty<string>(), errorMsg);

            field.AddRule(rule);

            return field;
        }

        /// <summary>
        /// 检查是否在指定值列表中
        /// </summary>
        /// <typeparam name="TFieldValue"></typeparam>
        /// <param name="field"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static IRequestField<TFieldValue> In<TFieldValue>(this IRequestField<TFieldValue> field,
            params TFieldValue[] values)
        {
            var rule = new ValueInRule<TFieldValue>("", values);
      
            field.AddRule(rule);

            return field;
        }

        public static IRequestField<TFieldValue> In<TFieldValue>(this IRequestField<TFieldValue> field,string errorMsg,
            params TFieldValue[] values)
        {
            var rule = new ValueInRule<TFieldValue>(errorMsg, values);

            field.AddRule(rule);

            return field;
        }



        public static IRequestField<T> AddToModelState<T>(this IRequestField<T> field, ModelStateDictionary modeState)
        {
            foreach (var rule in field.Rules)
            {
                var ret = rule.Validate(field);

                if (!ret)
                {
                    modeState.AddModelError(field.FieldName, ret.Message);
                }
            }

            return field;
        }

        public static IRequestArrayField<IEnumerable<TInputValue>, TInputValue> AddToModelState<TInputValue>(this IRequestArrayField<IEnumerable<TInputValue>, TInputValue> field, ModelStateDictionary modeState)
        {
            if (field.Rules.HasData())
            {
                foreach (var rule in field.Rules)
                {
                    var ret = rule.Validate(field);

                    if (!ret)
                    {
                        modeState.AddModelError(field.FieldName, ret.Message);
                    }
                }
            }
            
            //var arrayField = (RequestArrayField<TInputValue>) field;

            if (field.ElementFields.HasData())
            {
                foreach (var elementField in field.ElementFields)
                {
                    foreach (var rule in elementField.Rules)
                    {
                        //for (int i = 0; i < arrayField.ElementFields.Length; i++)
                        //{
                            var ret = rule.Validate(elementField);

                            if (!ret)
                            {
                                modeState.AddModelError(field.FieldName, ret.Message);
                            }
                        //}
                    }
                }
                           
            }



            return field;
        }

        public static void AddToModelState(this HttpRequest request, ModelStateDictionary modeState)
        {
            
            var fields =
                (RequestFieldCollection) request.HttpContext.RequestServices.GetService(typeof(RequestFieldCollection));

            foreach (var field in fields.GetAllFields())
            {
                foreach (var rule in field.Rules)
                {
                    var ret = rule.Validate(field);

                    if (!ret)
                    {
                        modeState.AddModelError(field.FieldName, ret.Message);
                    }
                }
            }
            
        }


        private static IRequestField<TFieldValue> addCompareRule<TFieldValue, TCompareValue>(IRequestField<TFieldValue> field, TCompareValue compareValue, CompareOpt opt, string errorMsg = null) where TCompareValue : IComparable where TFieldValue : IComparable
        {
            var rule = new ComparableValueRule<TFieldValue>((TFieldValue)Convert.ChangeType(compareValue, typeof(TFieldValue)), opt, errorMsg);

            field.AddRule(rule);

            return field;
        }

        private static IRequestField<TFieldValue?> addCompareRule2<TFieldValue, TCompareValue>(IRequestField<TFieldValue?> field, TCompareValue compareValue, CompareOpt opt, string errorMsg = null) where TCompareValue : struct where TFieldValue : struct
        {
            var rule = new ComparableValueRuleNullable<TFieldValue>((TFieldValue)Convert.ChangeType(compareValue, typeof(TFieldValue)), opt, errorMsg);

            field.AddRule(rule);

            return field;
        }

        private static IRequestField<TFieldValue?> addCompareRule3<TFieldValue, TCompareValue>(IRequestField<TFieldValue?> field, TCompareValue? compareValue, CompareOpt opt, string errorMsg = null) where TCompareValue : struct where TFieldValue : struct
        {
            var rule = new ComparableValueRuleNullable<TFieldValue>((TFieldValue?)Convert.ChangeType(compareValue, typeof(TFieldValue?)), opt, errorMsg);

            field.AddRule(rule);

            return field;
        }

    }
}